#include <assert.h>

#include "common.h"
#include "parser.h"

#define MAX_ID_CHARS 3

using namespace std;

Parser::LexicalAnalyzer::LexicalAnalyzer (const char* filePath) : id (0)
{
    fileStream.open (filePath);    
    assert (fileStream.good());
    
    ReadNextChar();
}

Parser::LexicalAnalyzer::~LexicalAnalyzer()
{
    fileStream.close();
}

void Parser::LexicalAnalyzer::SkipWhiteSpaces()
{    
    while (!fileStream.eof() &&
           currChar == ' '  || 
           currChar == '\t' || 
           currChar == '\n')
        ReadNextChar();
}


Parser::LexicalAnalyzer::Token Parser::LexicalAnalyzer::Scan()
{        
    if (fileStream.eof())
        return LexicalAnalyzer::End;
                                      
    SkipWhiteSpaces();
    
    // Check if currChar is an identifier
    if (IsChar (currChar)) {  
        stringBuf.str ("");
        
        while (!fileStream.eof() && IsChar (currChar)) {
            stringBuf << currChar;
            ReadNextChar();
        }
        return LexicalAnalyzer::Id;
    }
        
    Token t;
    switch (currChar) {            
        case '=':
            t = Equal;
            break;
            
        case ';':
            t = Semicolon;
            break;
            
        case ',':
            t = Comma;
            break;
            
        case '(':
            t = LeftPar;
            break;                
            
        case ')':
            t = RightPar;
            break;
            
        default:                
            reportError ("Unrecognized character %c", currChar);
            break;
    }
    ReadNextChar();  
    return t;    
}

Parser::LexicalAnalyzer::Token Parser::LexicalAnalyzer::GetNextToken()
{
    return Scan();
}

const string Parser::LexicalAnalyzer::GetTokenString()
{
    return stringBuf.str();
}

const char* Parser::LexicalAnalyzer::GetTokenChars()
{        
    stringBuf.seekp(0, ios::end);
    stringstream::pos_type offset = stringBuf.tellp();
    char* s = new char[(int)offset + 1 + MAX_ID_CHARS];
    stringBuf.read (s, offset);
    
    sprintf (s+offset, "%d", id++);       
//     s[offset] = '\0';
    return s;
}









Parser::Parser (const char* filePath) : scanner (filePath)
{       
}

Parser::~Parser()
{
}

void Parser::Match (LexicalAnalyzer::Token t)
{
    if (currToken == t) {
     
#ifdef DEBUG
        switch (currToken) {
            case LexicalAnalyzer::Id: 
                debug ("Id ");
                break;
                
            case LexicalAnalyzer::Equal: 
                debug ("= ");
                break;
                
            case LexicalAnalyzer::LeftPar: 
                debug ("( ");
                break;
                
            case LexicalAnalyzer::RightPar: 
                debug (") ");
                break;
                
            case LexicalAnalyzer::Semicolon: 
                debug (";\n");
                break;
                
            case LexicalAnalyzer::Comma: 
                debug (", ");
                break;
        }        
#endif
        currToken = scanner.GetNextToken();
    }
    else
        reportError ("Syntax Error");
}

void Parser::Stmt()
{
    Match (LexicalAnalyzer::Id);          
    
    const string& variable = scanner.GetTokenString();
    
    Match (LexicalAnalyzer::Equal);
    Match (LexicalAnalyzer::Id);
    
    // Create a new node for the call graph
    Function* f = new Function (scanner.GetTokenChars());
    aliases[variable] = f;    
    funcStack.push (f);
    graph->AddNode (f);
    
    // Resolve alias dependencies
    list<Function*>& dep = aliasDependences[variable];
    list<Function*>::iterator it = dep.begin();
    while (it != dep.end()) {
        f->AddDependent (*it);
        (*it)->dependencies++;
        it++;
    }
    Params();
    Match (LexicalAnalyzer::Semicolon);
}

void Parser::Params() 
{    
    Match (LexicalAnalyzer::LeftPar);
    Args();    
    funcStack.pop();    
    Match (LexicalAnalyzer::RightPar);
}

void Parser::Args()
{    
    if (currToken == LexicalAnalyzer::RightPar)       
        return;    
    Match (LexicalAnalyzer::Id);
    List();
}

void Parser::List()
{            
    if (currToken == LexicalAnalyzer::LeftPar) {
        // Create a new node for the call graph
        Function* f = new Function (scanner.GetTokenChars()); 
        f->AddDependent (funcStack.top());
        funcStack.top()->dependencies++;
        funcStack.push (f);
        graph->AddNode (f);

        Params();
        Ext(); 
    }
    else {        
        const string& variable = scanner.GetTokenString();
        Function* alias = aliases[variable];
        if (alias) { // If there is an alias
            alias->AddDependent (funcStack.top());
            funcStack.top()->dependencies++;
        }
        else {
            // Save the unsolved dependencies for the current variable
            aliasDependences[variable].push_back (funcStack.top());
        }
    
        if (currToken == LexicalAnalyzer::Comma) {                       
            Match (LexicalAnalyzer::Comma);
            Match (LexicalAnalyzer::Id); 
            List();
        }
    }
        
}

void Parser::Ext()
{
    if (currToken == LexicalAnalyzer::RightPar)
        return;    
    Match (LexicalAnalyzer::Comma);
    Match (LexicalAnalyzer::Id);            
    List();
}

CallGraph* Parser::Parse()
{    
    graph = new CallGraph();
    currToken = scanner.GetNextToken();
    
    while (currToken != LexicalAnalyzer::End)
        Stmt();
    
    return graph;
}


